developments

테스트에 대한 오해와 사실

15 min read|23. 2. 24.

misconception-and-facts-about-testing

테스트 코드를 작성하는 이유는 하나지만 작성하지 않은 이유는 저마다 다양하다.

개발자라면 '테스트 코드 작성'이라는 마음의 짐이 있다. 그리고 보통 그 짐을 다시 꺼내보진 않는다. 일정 주기마다 '테스트라이팅'(테스트 + 가스라이팅의 합성어)을 당해왔기 때문에 테스트 코드를 작성해야 한다는 생각은 있지만, 막상 작성하려고 하면 귀찮기도 하고 어렵기도 하고 당장 구현해야할 기능은 쌓여있기에 늘 그랬듯, 미룬다. 미뤄도 당장 문제가 발생하지 않기 때문에 쿨하게 넘어간다. 이러한 경험을 몇번 반복한 뒤에 고민을 하기 시작했다.

테스트는 왜 작성하기 어려운가?

테스트 코드의 중요성, 작성해야 하는 이유 말고 이 이야기를 해보려고 한다. 테스트에 대한 고민을 한방에 해결해줄 수 있는 글을 기대했다면 아쉽게도 이 글은 답이 될 수 없다. 그저 조금 더 근본적인 이유에 가까워 지기 위한 물음들로 구성되어 있을 뿐이고 또 하나의 '테스트라이팅' 글일 뿐이다.

이 글은 총 2편의 글로 나뉘어져 있다.

(이 글에서 말하는 테스트는 테스트 코드를 활용한 자동화 테스트를 의미한다.)

Table of contents

  • 오해 1. 테스트의 목적은 버그의 박멸이다.
  • 오해 2. 테스트를 작성할 때, 제품 코드에는 변경이 발생하면 안된다.
  • 오해 3. 테스트하기 좋은 코드가 좋은 코드이다.
  • 오해 4. 테스트 커버리지는 의미가 없다.
  • 오해 5. 테스트 코드는 없는 것보단 낫다.

테스트에 대한 오해와 사실

테스트에 대해 이야기하기 전에, 오해하고 있는 것을 바로 잡을 필요가 있다. 하나씩 살펴보자.

오해 1. 테스트의 목적은 버그의 박멸이다.

테스트는 현재 인지하고 있는 플로우 내에서 버그가 있는지 없는지 알려준다. 하지만 예상치 못한 버그가 있는지는 알려주지 않는다. 즉 테스트 코드로 작성되어 있는 범위 내에서 의도한 대로 동작하고 있는지 알려주지만,(존재) 예상하지 못한 버그가 있는지(부재)에 대해서는 미리 알 수 없다. 버그가 있다는 것을 미리 알았더라면 테스트 코드로부터 인지하기 전에 고치지 않았을까?

이것은 버그를 사전에 방지하는데 집중하지 말자는 것을 의미한다.

테스트 케이스를 작성하면서 '발생할 수 있는 모든 케이스를 전부 대응했나?' 길게 고민하는 대신, 테스트 코드에 들어가는 시간을 줄이는 것이 효율적이다. 놓치게 되는 테스트 케이스는 QA 과정을 거치면서 발견되던가 사용자로부터 사용되면서 발견될 수 있다. 대신 발견된 버그는 코드로 재현하고 그 존재를 테스트 케이스로 추가하여 트래킹 해야 한다. 이런 과정을 거치면서 경험치가 쌓이고 놓치는 케이스가 점점 적어질 것이라고 생각한다.

테스트는 버그의 존재를 알려줄 뿐, 부재를 알려주지 않는다.

오해 2. 테스트를 작성할 때, 제품 코드에는 변경이 발생하면 안된다.

이 문장은 반은 맞고 반은 틀리다. 보통 다음과 같은 경우 잘못된 접근이다.

  • 테스트하기 위해 어떤 값이나 속성이 추가되는 경우
  • 테스트하기 위해 노출되고 있지 않던 값을 노출하는 경우

기능이 아닌 테스트만을 위한 무언가가 제품 코드에 추가되는 것은 대부분 옳지 않으며, 이를 코드 오염 (code pollution)이라고 한다. 프론트엔드 어플리케이션을 테스트 하다 보면 가끔 data-testid를 추가하고 싶은 욕망이 생기는데, 대부분의 경우 제품 코드가 잘 작성되어 있다면 필요없는 속성이다. testid없이 테스트가 이루어질 수 없다면 무언가 잘못되었거나 테스트 케이스가 필요한 것인지 의심해볼 수 있다. 맞는 비유가 될 지는 모르겠지만 이것은 마치 TypeScript를 사용하면서 타입에 any를 사용하는 것과 비슷하다.

애써 캡슐화한 로직을, 상태를 따로 테스트하기 위해 노출하는 것은 잘못된 결정이다. 주객이 전도된 경우이기 때문이다. 외부로 공개된 인터페이스를 통해 테스트 할 순 없는지 고민해볼 필요가 있는 케이스이다. 테스트 코드는 원딜에게 cs를 양보하는 서포터와 같다. 제품 코드가 발전할 수 있도록 지원하는 역할이라는 것을 잊으면 안 된다.

하지만 제품 코드를 수정하는 것이 맞는 방향일 때가 있는데, 바로 이어지는 내용의 경우이다.

오해 3. 테스트하기 좋은 코드가 좋은 코드이다.

좋은 코드란 무엇인가? 이야기를 하다 보면 '테스트하기 좋은 코드'가 좋은 코드라는 이야기가 나온다. 닭이 먼저냐, 달걀이 먼저냐 이야기를 할 수 있겠지만 이것은 엄연히 따지면 앞뒤가 바뀐 말이다. 테스트하기 좋은 코드가 좋은 코드가 아니라 테스트를 '잘' 작성하려다 보면 좋은 코드가 만들어지는 것이다.

테스트 하기 좋다는 것은 좋은 코드의 조건을 충족시키는 것이지만 테스트에 대한 고민없이 좋은 코드가 나오긴 힘들다. 좋은 코드, 좋은 설계의 조건이 되는 인터페이스를 통한 통신, 구현을 캡슐화 하는 것 등은 테스트 코드를 작성하려고 할 때 쉽게 발견할 수 있는 것들이다. 테스트 코드 없이 이러한 것들을 미리 파악할 수 있다면 테스트하기 좋은 코드가 좋은 코드라고 할 수 있겠지만 쉽지 않다.

테스트 코드를 작성하려다 보면 자연스럽게 설계가 수정된다. 여러 책임을 갖고 있다면 한 가지의 책임을 가질 수 있도록 분해를 하거나, 순수 함수로 기능을 표현하거나 의존 관계를 끊어내거나 더 좋은 방향으로 설계를 수정할 수 있다. 테스트를 작성하면서 잘못된 설계를 인정하고 바로잡자.

테스트를 잘 작성하려다 보면 좋은 코드가 만들어진다.

오해 4. 테스트 커버리지는 의미가 없다.

테스트 커버리지는 의미가 없다고 한다. 이것은 반은 맞고 반은 틀린 이야기이다. 우선 커버리지는 크게 코드 커버리지, 분기 커버리지 두 가지로 구분지을 수 있다.

  • 코드 커버리지는 전체 line 수, 테스트 코드가 실행되면서 실행된 라인 수를 기준으로 측정된다.
  • 분기 커버리지는 테스트 코드가 실행되면서 검증한 분기 기준으로 측정한다.

테스트 코드가 제품의 모든 코드를 커버할 수 없게 되는데 그래도 괜찮은 것일까? 두 커버리지가 어떻게 측정하는지 곱씹어보면 단순히 커버리지 수치가 높은 게 중요하지 않다는 것을 알 수 있다. 두 커버리지 모두 빈틈이 있기 때문이다. 이 테스트 커버리지가 중요하지 않다는 것을 문자 그대로보다 다른 의미로 해석할 때 더 빛을 발하게 되는데, 바로 _'모든 코드를 테스트할 필요없다'_라는 의미로 해석하는 것이다. 테스트 코드가 모든 line과 분기를 테스트하지 않아도 된다는 것이다. 그렇다면 우리는 무엇을 테스트 할 것인가 고민으로 이어질 수 있다.

어느 정도 테스트 코드가 작성되어 성숙한 단계에 접어든 프로젝트에서는 의미가 없을 수 있지만 테스트 코드가 전혀 없는 프로젝트에서는 나름의 의미가 있을 수 있다. 이제 막 테스트 코드를 도입하려고 하는 프로젝트에서는 최소한의 목표치를 설정하기 위해 설정하면 어떨까? 초기에 60% 정도를 목표로 잡을 때는 의미가 있을 수 있다. 이것은 측정 가능한 정량적 목표일 뿐 정성적 목표, 좋은 테스트 코드를 작성하는데 더 집중하자.

그렇다면 무엇을 테스트해야 할까? 좋은 테스트 코드는 무엇일까? 이 질문으로 이어진다.

커버리지보다 무엇을 테스트할지, 어떻게 테스트할지 더 고민하자.

오해 5. 테스트 코드는 없는 것보단 낫다.

'없는 것보다 낫지'라고 생각하고 테스트 코드를 작성하는 경우를 종종 만날 수 있다. 하지만 무책임하게 작성된 테스트 코드는 제품 변경에 발목을 잡는 성가신 코드 덩어리가 되곤 한다. 이런 테스트 코드는 결국 사라지거나 처음부터 다시 작성하게 된다. 이러한 경험들이 쌓이면서 테스트 코드는 딱히 도움되지 않아라는 러닝이 쌓이고 오히려 테스트와 더 멀어지게 된다.

코드에도 좋은 코드가 있듯이, 테스트 코드에도 좋은 테스트 코드는 따로 있다. 좋은 테스트가 있을 때 비로소,

  • 변경에 안심할 수 있다.
  • 밟았던 이슈는 또 밟지 않을 수 있다.
  • 리팩토링에 자신감이 생긴다.
  • 테스트가 문서가 된다.
  • 자연스럽게 웹 접근성을 지키게 된다.
  • 궁극적으로 더 빠르게 개발할 수 있게 된다.

단 완벽한 테스트 코드는 없다. 트레이드 오프를 계산해서 좋은 테스트 코드를 작성해야 한다.

좋은 테스트 코드는 따로 있다.

마무리

테스트에 대한 이야기를 5가지 주제를 잡아 정리해봤다. 그래서 좋은 테스트 코드가 정확히 무엇인지는 다음 포스트에서 이야기 할 예정이다.

NEXT: 프론트엔드 테스트 코드와 의존성